package com.gitee.l0km.casban;

import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.base.Predicates.and;
import static com.google.common.base.Predicates.notNull;

import java.lang.annotation.Annotation;
import java.lang.reflect.AccessibleObject;
import java.lang.reflect.AnnotatedElement;
import java.lang.reflect.Member;
import java.util.Arrays;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.Set;
import java.util.concurrent.TimeUnit;

import com.gitee.l0km.aocache.annotations.AoWeakCacheable;
import com.gitee.l0km.casban.exception.PackageScanException;
import com.gitee.l0km.common.spring.core.annotation.MergedAnnotation;
import com.gitee.l0km.common.spring.core.annotation.RepeatableContainers;
import com.gitee.l0km.common.spring.core.annotation.TypeMappedAnnotations;
import com.gitee.l0km.common.spring.core.annotation.MergedAnnotations.SearchStrategy;
import com.google.common.base.Function;
import com.google.common.base.Predicate;
import com.google.common.base.Predicates;
import com.google.common.base.Strings;
import com.google.common.base.Supplier;
import com.google.common.collect.FluentIterable;
import com.google.common.collect.Sets;

/**
 * 提供基于反射的类扫描和成员查找工具方法集合。
 * 
 * <p>主要功能包括：
 * <ul>
 *   <li>查找满足条件的类集合</li>
 *   <li>查找类中的成员(字段、方法)</li>
 *   <li>处理注解相关操作</li>
 *   <li>支持多种过滤条件组合</li>
 * </ul>
 * 
 * <p>典型用法示例：
 * <pre>{@code
 * // 查找所有带有特定注解的类
 * Set<Class<?>> classes = CasbanScanners.findClasses(
 *     classLoader.getClasses(),
 *     () -> CountLimitStrategy.ANY,
 *     clazz -> clazz.isAnnotationPresent(MyAnnotation.class));
 * 
 * // 查找类中带有特定注解的方法
 * Set<Member> methods = CasbanScanners.findMembersOfClass(
 *     MyClass.class,
 *     member -> member.isAnnotationPresent(MyMethodAnnotation.class));
 * }</pre>
 * 
 * <p>注意事项：
 * <ul>
 *   <li>本类方法都是线程安全的</li>
 *   <li>部分方法使用了缓存优化性能</li>
 *   <li>当类路径中存在缺失的依赖时可能抛出NoClassDefFoundError</li>
 * </ul>
 * 
 * @author guyadong
 * @since 1.1.0
 */
public class CasbanScanners {

	@AoWeakCacheable(expireAfterAccess = 60, expireAfterAccessTimeUnit = TimeUnit.SECONDS)
	public static Set<Class<?>> findClasses(Iterable<Class<?>> classes,
				Supplier<RangeFilter> finisher, Iterable<Predicate<Class<?>>> filters){
		checkNotNull(classes,"classes is null");
		LinkedHashSet<Predicate<Class<?>>> _filters = Sets.newLinkedHashSet(
				Arrays.asList(Predicates.<Class<?>>notNull(),	
						CommonPredicates.publicClassFilter()));
		if(filters != null) {
			FluentIterable.from(filters).filter(notNull()).copyInto(_filters);
		}
		LinkedHashSet<Class<?>> filtered = FluentIterable.from(classes)
				.filter(and(_filters))
				.copyInto(new LinkedHashSet<Class<?>>());
		if(null == finisher) {
			finisher = CountLimitStrategy.ANY;
		}
		return finisher.get().checkSize(filtered);
	}

	@SafeVarargs
	public static Set<Class<?>> findClasses(Iterable<Class<?>> classes,
			Supplier<RangeFilter> finisher, Predicate<Class<?>>... filters){
		return findClasses(classes,finisher,null == filters ? null : FluentIterable.from(filters));
	}

	@AoWeakCacheable(maximumSize = 200, expireAfterAccess = 60, expireAfterAccessTimeUnit = TimeUnit.SECONDS)
	public static Set<Member> findMembersOfClass(Class<?> clazz,
			Iterable<Predicate<Member>> filters){
		checkNotNull(clazz,"classes is null");
		LinkedHashSet<Predicate<Member>> _filters = 
				Sets.newLinkedHashSet(Arrays.asList(Predicates.<Member>notNull()));
		if(filters != null) {
			FluentIterable.from(filters).filter(notNull()).copyInto(_filters);
		}
		Predicate<Member> _and = and(_filters);
		LinkedHashSet<Member> result = new LinkedHashSet<>();
		try {
			FluentIterable.from(clazz.getFields()).filter(_and).copyInto(result);
			FluentIterable.from(clazz.getMethods()).filter(_and).copyInto(result);
		} catch (NoClassDefFoundError e) {
			/** 可能会遇到找不到方法或字段中涉及的类的问题，比如某些依赖以provided方式提供 */
			// DO NOTHING
			throw e;
		}
		return result;
	}
	@SafeVarargs
	public static Set<Member> findMembersOfClass(Class<?> clazz,
			Predicate<Member>... filters){
		return findMembersOfClass(clazz,null == filters ? null: Arrays.asList(filters));
	}

	public static<A extends AccessibleObject> Set<A> findMembersOfClass(Class<?> clazz,
			Function<Member, A> as,
			Iterable<Predicate<Member>> filters){
		return FluentIterable.from(findMembersOfClass(clazz,filters))
				.transform(checkNotNull(as,"as is null"))
				.filter(Predicates.notNull())
				.copyInto(new LinkedHashSet<A>());
	}
	@SafeVarargs
	public static<A extends AccessibleObject> Set<A> findMembersOfClass(Class<?> clazz,
			Function<Member, A> as,
			Predicate<Member>... filters){
		return findMembersOfClass(clazz,as,null == filters ? null: Arrays.asList(filters));
	}

	public static Member findMember(Class<?> clazz,
			boolean onlyOne,
			Iterable<Predicate<Member>> filters){
		Set<Member> members = findMembersOfClass(clazz,filters);
		return fetchClassOrnull(members,onlyOne,"member");
	}

	@SafeVarargs
	public static Member findMember(Class<?> clazz,
			boolean onlyOne,
			Predicate<Member>... filters){
		return findMember(clazz, onlyOne, null == filters ? null: Arrays.asList(filters));
	}

	public static<A extends AccessibleObject> A findAccessibleObject(Class<?> clazz,
			boolean onlyOne,
			Function<Member, A> as,
			Iterable<Predicate<Member>> filters){
		Set<A> members = findMembersOfClass(clazz,as,filters);
		return fetchClassOrnull(members,onlyOne,"member");
	}
	@SafeVarargs
	public static<A extends AccessibleObject> A findAccessibleObject(Class<?> clazz,
			boolean onlyOne,
			Function<Member, A> as,
			Predicate<Member>... filters){
		return findAccessibleObject(clazz,onlyOne,as,null == filters ? null: Arrays.asList(filters));
	}
	public static Member findMember(Iterable<Member> input,
			boolean onlyOne, Iterable<Predicate<Member>> memberFilters){
		Set<Member> members = findMembers(input,
				onlyOne? CountLimitStrategy.ONLY_ONE:CountLimitStrategy.MOST_ONE,
						memberFilters);
		return members.isEmpty() ? null : members.iterator().next();
	}

	public static Set<Member> findMembers(Class<?> clazz,
			Set<Member> result,
			Iterable<Predicate<Member>> filters){
		result.addAll(findMembersOfClass(clazz,filters));
		return result;
	}

	@SuppressWarnings("unchecked")
	public static Set<Member> findMembers(Class<?> clazz,
			Set<Member> result,
			Predicate<Member>... filters){
		return findMembers(clazz,result,null == filters ? null : FluentIterable.from(filters));
	}

	public static Set<Member> findMembers(Iterable<Class<?>> classes,
			Supplier<RangeFilter> finisher, 
			Iterable<Predicate<Class<?>>> classFilters,
			Iterable<Predicate<Member>> memberFilters){
		Set<Class<?>> filteredClasses = findClasses(classes,CountLimitStrategy.ANY,classFilters);
		LinkedHashSet<Member> result = new LinkedHashSet<Member>();
		for(Class<?> clazz:filteredClasses) {
			findMembers(clazz,result,memberFilters);
		}
		if(null == finisher) {
			finisher = CountLimitStrategy.ANY;
		}
		return finisher.get().checkSize(result);
	}

	public static Member findMember(Iterable<Class<?>> classes,
			boolean onlyOne,
			Iterable<Predicate<Class<?>>> classFilters, Iterable<Predicate<Member>> memberFilters){
		Set<Member> members = findMembers(classes,
				onlyOne? CountLimitStrategy.ONLY_ONE:CountLimitStrategy.MOST_ONE,
				classFilters,memberFilters);
		return members.isEmpty() ? null : members.iterator().next();
	}

	public static Set<Member> findMembers(Iterable<Member> members,
			Supplier<RangeFilter> finisher, 
			Iterable<Predicate<Member>> filters){
		checkNotNull(members,"members is null");
		LinkedHashSet<Predicate<Member>> _filters = 
				Sets.newLinkedHashSet(Arrays.asList(Predicates.<Member>notNull()));
		if(filters != null) {
			FluentIterable.from(filters).filter(notNull()).copyInto(_filters);
		}
		Predicate<Member> _and = and(_filters);
		try {
			LinkedHashSet<Member> filtered = FluentIterable.from(members).filter(_and).copyInto(new LinkedHashSet<Member>());
			if(null == finisher) {
				finisher = CountLimitStrategy.ANY;
			}
			return finisher.get().checkSize(filtered);
		} catch (NoClassDefFoundError e) {
			/** 可能会遇到找不到方法或字段中涉及的类的问题，比如某些依赖以provided方式提供 */
			// DO NOTHING
			throw e;
		}
	}

	public static <T> T fetchClassOrnull(Set<T> classes,boolean onlyOne, String elementName){
		switch(classes.size()) {
		case 0:
			if(onlyOne) {
				throw new PackageScanException(
						"NOT FOUND OR FOUND matched "+String.valueOf(elementName));
			}
			return null;
		case 1:
			return classes.iterator().next();
		default:
			throw new PackageScanException(
					String.format("FOUND %s matched %s:%s",
							classes.size(),
							String.valueOf(elementName), 
							CommonFunctions.joinFun().apply(classes)));
		}
	}

	/**
	 * 获取可重复注解<br>
	 * 参考 {@link com.gitee.l0km.common.spring.core.annotation.AnnotatedElementUtils#findMergedRepeatableAnnotations(AnnotatedElement, Class)}实现,
	 * 支持注解为元注解，且定义的该元注解的注解也是可重复注解
	 * @param <A>
	 * @param annotatedElement
	 * @param annotationType
	 */
	public static <A extends Annotation> Iterable<MergedAnnotation<A>> findMergedRepeatableAnnotations(AnnotatedElement annotatedElement,Class<A>annotationType){
		RepeatableContainers repeatableContainers = RepeatableContainers.standardRepeatables();
		return TypeMappedAnnotations.from(annotatedElement, SearchStrategy.TYPE_HIERARCHY, repeatableContainers)
					.iterable(annotationType);
	}

	/**
	 * [递归]返回{@code annotationType}指定注解的方法(字段)上是否有 {@code targetType}指定的注解,
	 * 如果没有找到且{@code searchMeta}为{@code true}会递归搜索所有元注解<br>
	 * 输入参数为{@code null}或空返回{@code false}
	 * @param annotationType
	 * @param targetType 注解类名
	 * @param searchMeta 是否搜索元注解
	 */
	@AoWeakCacheable(maximumSize = 1000, expireAfterAccess = 60, expireAfterAccessTimeUnit = TimeUnit.SECONDS)
	public static boolean withAnnotation(Class<? extends Annotation> annotationType, String targetType, boolean searchMeta) {
		if(null != annotationType && !Strings.isNullOrEmpty(targetType)) {
			for(java.lang.reflect.Method method: annotationType.getMethods()) {
				if(com.gitee.l0km.common.spring.core.annotation.TypeMappedAnnotations
						.from(method).isDirectlyPresent(targetType)) {
					return true;
				}
			}
			if(searchMeta) {
				for(Iterator<com.gitee.l0km.common.spring.core.annotation.MergedAnnotation<Annotation>> 
				itor = com.gitee.l0km.common.spring.core.annotation.TypeMappedAnnotations
						.from(annotationType).iterator();itor.hasNext();) {
					if(withAnnotation(itor.next().getType(), targetType, searchMeta)) {
						return true;
					}
				}
			}
		}
		return false;
	}

	/**
	 * [递归]返回{@code annotationType}指定注解的方法(字段)上是否有 {@code targetType}指定的注解,
	 * 如果没有找到且{@code searchMeta}为{@code true}会递归搜索所有元注解<br>
	 * 输入参数为{@code null}或空返回{@code false}
	 * @param annotationType
	 * @param targetType
	 * @param searchMeta 是否搜索元注解
	 */
	public static boolean withAnnotation(Class<? extends Annotation> annotationType, Class<? extends Annotation> targetType, boolean searchMeta) {
		return withAnnotation(annotationType, null == targetType ? null :targetType.getName(), searchMeta);
	}

	/**
	 * 判断注解类型是否使用了casban定义的{@link com.gitee.l0km.common.spring.core.annotation.AliasFor}注解
	 * @param annotationType
	 */
	public static boolean withCasbanAliasFor(Class<? extends Annotation> annotationType) {
		return withAnnotation(annotationType,"com.gitee.l0km.common.spring.core.annotation.AliasFor", true);
	}

	/**
	 * 判断注解类型是否使用了spring定义的{@link com.gitee.l0km.common.spring.core.annotation.AliasFor}注解
	 * @param annotationType
	 */
	public static boolean withSpringAliasFor(Class<? extends Annotation> annotationType) {
		return withAnnotation(annotationType,"org.springframework.core.annotation.AliasFor", true);
	}
}
